中文意思是「測試驅動開發」,是一種「先寫測試再寫開發」的開發流程。
TDD 是一個 red-green-refactor
的 cycle,它的概念就是:先寫好測試,當然這時候的測試是 fail 的(紅燈),再基於測試,補上必要的程式碼,讓測試 pass(綠燈),之後可以重構程式碼,重複「測試亮紅燈,修復後再亮綠燈」的過程。
透過先寫測試,事先釐清元件應該具備那些功能、應該測試哪些功能,再回頭補上元件應該有的功能,這種開發方式,更能確保元件是經過思考與設計的。
我們現在試著用 TDD 的方式來開發一個表單。
這個表單主要功能是發表文章,它接收標題 (title) 、內容 (content)、標籤 (tag),並且有一個按鈕可以點擊送出表單,並發出 HTTP request。
我們會經歷以下步驟,一步一步先寫測試再寫程式,重複亮紅燈再亮綠燈的循環過程:
在測試檔中引入元件及 React Testing Library,並命名測試名稱 'renders a form with title, content, tags, and a submit button'
:
tests/post-editor.js
import React from 'react'
import {Editor} from '../post-editor'
import {render} from '@testing-libraray/react'
test('renders a form with title, content, tags, and a submit button', () => {
<Editor />
})
使用 getBYLabelText
、 getByText
測試元件是否 render 在畫面上:
tests/post-editor.js
test('renders a form with title, content, tags, and a submit button', () => {
const {getBYLabelText, getByText} = render(<Editor />)
getByLabelText(/title/i)
getByLabelText(/content/i)
getByLabelText(/tags/i)
getByText(/submit/i)
})
這時候測試是 fail 的,因為我們根本還沒開始寫 <Editor />
這個元件,我們只是先定義好要測試什麼:
現在我們開始寫 <Editor />
, 一個一個補上測試內定義的條件,讓測試亮綠燈:
post-editor.js
import React from 'react'
function Editor() {
return (
<form>
<label htmlFor="title-input">Title</label>
<input id="title-input" />
<label htmlFor="content-input">Content</label>
<textarea id="content-input" />
<label htmlFor="tags-input">Tags</label>
<textarea id="tags-input" />
<button type="submit">Submit</button>
</form>
)
}
export {Editor}
這樣測試就亮綠燈了,完成表單目前的結構開發。
完成表單結構之後,接下來要加上 submit button 的功能:點擊 submit button 之後,submit button 要 disabled。
首先,回到測試檔案,我們加上測試:
getByText(/submit/i)
取得回傳的 submit buttonfireEvent.click()
觸發點擊事件expect().toBeDisabled()
驗證 submit button 是否 disabledtests/post-editor.js
import React from 'react'
import {render, fireEvent} from '@testing-library/react' // 引入 fireEvent
import {Editor} from '../post-editor'
test('renders a form with title, content, tags, and a submit button', () => {
const {getByLabelText, getByText} = render(<Editor />)
getByLabelText(/title/i)
getByLabelText(/content/i)
getByLabelText(/tags/i)
const submitButton = getByText(/submit/i)
fireEvent.click(submitButton) // 點擊
expect(submitButton).toBeDisabled() // 驗證 disabled
})
更新完上面的測試,現在跑測試會是 fail 亮紅燈 ❌。
我們回到 post-editor.js
檔案,補上需要的功能:
onSubmit
事件及它的 handler handleSubmit
handleSubmit
function 裡面加上 e.preventDefault()
阻止預設的 submit 後換頁isSaving
這個 state,並在 handleSubmit
function 裡 setIsSaving(true)
disabled={isSaving}
,設置 disabled 屬性基於 isSaving
這個 statepost-editor.js
function Editor() {
const [isSaving, setIsSaving] = React.useState(false)
function handleSubmit(e) {
e.preventDefault()
setIsSaving(true)
}
return (
<form onSubmit={handleSubmit}>
...
<button type="submit" disabled={isSaving}>Submit</button>
)
}
測試再度亮起綠燈 ✅